using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace WPFTemplateLib.Mvvm
{
	/// <summary>
	/// 可观测对象，实现了属性变动通知接口
	/// </summary>
	/// <example>
	/// <code>
	/// class Sample : ObservableObject
	/// {
	///     private string _UserName;
	///     public string UserName
	///     {
	///         get => _UserName;
	///         set => SetProperty(ref _UserName, value);
	///     }
	/// }
	/// </code>
	/// </example>
	public class ObservableObject : INotifyPropertyChanged, INotifyPropertyChanging
	{
		#region 构造函数

		protected ObservableObject()
		{
			PropertyChanged += PropertyChangedHandle;
		}

		~ObservableObject()
		{
			PropertyChanged -= PropertyChangedHandle;
		}

		#endregion

		#region 绑定基础

		public event PropertyChangedEventHandler PropertyChanged;
		public event PropertyChangingEventHandler PropertyChanging;

		/// <summary>
		/// 属性变动通知
		/// </summary>
		/// <param name="propertyName">属性名</param>
		protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
		{
			PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
		}

		/// <summary>
		/// 属性即将变动通知
		/// </summary>
		/// <param name="propertyName">属性名</param>
		protected void OnPropertyChanging([CallerMemberName] string propertyName = null)
		{
			PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(propertyName));
		}

		protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
		{
			if(Equals(storage, value))
			{
				PropertySetIntentionHandle(propertyName, false);
				return false;
			}

			OnPropertyChanging(propertyName);
			storage = value;
			OnPropertyChanged(propertyName);

			PropertySetIntentionHandle(propertyName, true);
			return true;
		}

		protected bool SetPropertyWithoutCompare<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
		{
			OnPropertyChanging(propertyName);
			storage = value;
			OnPropertyChanged(propertyName);

			PropertySetIntentionHandle(propertyName, null);
			return true;
		}

		#endregion

		#region 其它方法

		/// <summary>
		/// 属性变动处理方法（子类重写，依据属性名来判断并触发需要的操作）
		/// </summary>
		protected virtual void PropertyChangedHandle(object sender, PropertyChangedEventArgs e)
		{
			//子类重写
		}

		/// <summary>
		/// 属性设置意图处理方法（无论设置后值是否变换都触发）（子类重写，依据属性名来判断并触发需要的操作）
		/// </summary>
		/// <remarks>
		///	属性需要使用了 <see cref="SetProperty{T}"/> 等方法，此方法才有效。
		/// 另外，很多情况下，如果设置的属性值是相同的，根本不会进 Set 等方法，这种情况下此方法也无能为力，具体请自行测试。
		/// </remarks>
		/// <param name="propName">属性名</param>
		/// <param name="isChanged">是否变化（true-变化，false-未变化，null-未知）</param>
		protected virtual void PropertySetIntentionHandle(string propName, bool? isChanged)
		{
			//子类重写
		}

		#endregion
	}
}
